﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace XSerialization
{
    public interface IPattern
    {
        bool Match(object o, XSerializerContext c);
        void Process(object o, XSerializerContext c);
    }

    public class NullPattern: IPattern
    {
        public bool Match(object o, XSerializerContext c)
        {
            return o == null;
        }

        public void Process(object o, XSerializerContext c)
        {
            // do nothing
        }
    }

    public class EnumerablePattern : IPattern
    {
        public bool Match(object o, XSerializerContext c)
        {
            return o is IEnumerable;
        }

        public void Process(object o, XSerializerContext c)
        {
            foreach (var v in o as IEnumerable)
            {
                c.Writer.WriteStartElement(Naming.GetObjectName(v));
                c.Serialize(v);
                c.Writer.WriteEndElement();
            }
        }
    }

    public class ObjectPattern : IPattern
    {
        public bool Match(object o, XSerializerContext c)
        {
            return true;
        }

        public void Process(object o, XSerializerContext c)
        {
            c.ProcessedObjects.Add(o);
            foreach (var p in o.GetType().GetProperties())
            {
                var v = p.GetValue(o, null);
                c.Writer.WriteStartElement(Naming.GetPropertyName(p));
                c.Serialize(v);
                c.Writer.WriteEndElement();
            }
        }
    }

    public class SimplePattern: IPattern
    {
        public bool Match(object o, XSerializerContext c)
        {
            return IsSimpleType(o);
        }

        public void Process(object o, XSerializerContext c)
        {
            c.Writer.WriteValue(o);
        }

        private IEnumerable<Type> GetBuildInTypes()
        {
            yield return typeof(System.Boolean);
            yield return typeof(System.Byte);
            yield return typeof(System.SByte);
            yield return typeof(System.Char);
            yield return typeof(System.Decimal);
            yield return typeof(System.Double);
            yield return typeof(System.Single);
            yield return typeof(System.Int32);
            yield return typeof(System.UInt32);
            yield return typeof(System.Int64);
            yield return typeof(System.UInt64);
            yield return typeof(System.Object);
            yield return typeof(System.Int16);
            yield return typeof(System.UInt16);
            yield return typeof(System.String);
        }

        private Type BuildNullableType(Type t)
        {
            return typeof(Nullable<>).MakeGenericType(t);
        }

        private bool IsSimpleType(object obj)
        {
            return GetBuildInTypes()
                .Concat(GetBuildInTypes().Where(t => t.IsValueType).Select(BuildNullableType))
                .Contains(obj.GetType());
        }
    }

    public class CircularReferencePattern: IPattern
    {
        public bool Match(object o, XSerializerContext c)
        {
            return c.ProcessedObjects.Contains(o);
        }

        public void Process(object o, XSerializerContext c)
        {
            // do nothing
        }
    }
}
